/+
Library dbapi - Simple database api for the D programming language.

Copyright (C) 2007 Myron Alexander (myron.alexander...gmail.com)

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+/

module dbapi.internal.exceptionproperties;

import std.string : str = toString, toupper, tolower;

static string excVarName (string propertyName) {
   return tolower(propertyName[0..1]) ~ propertyName[1..$];
}

static string excKeyName (string propertyName) {
   return "K_" ~ toupper (propertyName);
}

static string excKeyCode (string keyName, string propertyName) {
   return "private static const final "~keyName~" = \""~propertyName~"\";";
}

static string excHasCode (string keyName, string propertyName) {
   return "bool has"~propertyName~" () {\n"
          "  return hasProperty ("~keyName~");\n"
          "}";
}

static string excProp ( string propertyType
                              , string propertyName
                              , string defVal       )
{
   string keyName = excKeyName (propertyName);
   string varName = excVarName (propertyName);

   string setCode =
      "typeof(this) set"~propertyName~" ("~propertyType~" "~varName~") {\n"
      "   setProperty ("~keyName~", "~varName~");\n"
      "   return this;\n"
      "}";

   string hasCode = excHasCode (keyName, propertyName);

   string getCode =
      propertyType~" "~varName~" () {\n"
      "   Box v = getProperty ("~keyName~");\n"
      "   if (v.type !is null) {\n"
      "      return unbox!("~propertyType~")(v);\n"
      "   } else {\n"
      "      return "~defVal~";\n"
      "   }\n"
      "}";

   string keyCode = excKeyCode (keyName, propertyName);

   return setCode ~ "\n\n" ~ hasCode ~ "\n\n" ~ getCode ~ "\n\n" ~ keyCode;
}

static string excProp (string propertyName)
{
   string keyName = excKeyName (propertyName);
   string varName = excVarName (propertyName);

   string setCode =
      "typeof(this) set"~propertyName~"(T) (T "~varName~") {\n"
      "   setProperty ("~keyName~", "~varName~");\n"
      "   return this;\n"
      "}";

   string hasCode = excHasCode (keyName, propertyName);

   string getCode =
      "Box "~varName~" () {\n"
      "   return getProperty ("~keyName~");\n"
      "}";

   string keyCode = excKeyCode (keyName, propertyName);

   return setCode ~ "\n\n" ~ hasCode ~ "\n\n" ~ getCode ~ "\n\n" ~ keyCode;
}

static string excPropC (string propertyName) {
   return excProp ("string", propertyName, "null");
}

static string excPropI (string propertyName) {
   return excProp ("int", propertyName, "0");
}

static string excPropB (string propertyName) {
   return excProp ("bool", propertyName, str (bool.init));
}

template ExceptionConstructor () {
   this (char[] msg) {super (msg);}
   this (char[] msg, Exception cause) {super (msg, cause);}

   /**
    * Override initCause to return the type of the instantiated
    * exception rather than the base exception. This is only necessary so
    * developers can set the cause before the properties.
    */
   override typeof(this) initCause (Exception cause) {
      super.initCause (cause);
      return this;
   }
   
   /**
    * Override setLongMsg to return the type of the instantiated
    * exception rather than the base exception.
    */
   override typeof(this) setLongMsg (string longMsg) {
      super.setLongMsg (longMsg);
      return this;
   }
}
